
#include "land_graphics.h"

#define LAND_2D_CORE

#include "init.h"


struct LandSprite{
    LandObj obj;
    LandFrame* frame;
    LandFrameSlot* slot;
    unsigned offset;
    unsigned count;
};

static struct {
    LandDraw draw;
    LandTexture* drawTex;
    unsigned texPos;
}ctx = {0};


static void
beginDraw(LandScene* scene) {
    landClearVertex(ctx.draw.vertex);
    ctx.drawTex = NULL;
    LandDraw* draw = &ctx.draw;
    draw->offset = 0;
    draw->count = 0;
    draw->view = &scene->view;
}

static void
readyDraw(LandScene* scene) {
    landUploadVertex(ctx.draw.vertex);
}

static void
preDraw(LandScene* scene, LandObj* obj) {
    LandSprite* sprite = (LandSprite*)obj;
    if (!sprite->slot) {
        return;
    }
    LandFrameSlot* slot = sprite->slot;
    LandDraw* draw = &ctx.draw;
    LandVector* color = &sprite->obj.color;
    LandVector* rect = &obj->rect;
    LandVector2D* size = &obj->size;

    LandFloat rw = size->width / slot->width;
    LandFloat rh = size->height / slot->height;
    LandFloat xpos = rect->left;
    LandFloat ypos = rect->top;

    LandVector* sRect = &slot->rect;
    LandVector* border = &slot->border;
    LandFloat fw = sRect->right * rw;
    LandFloat fh = sRect->bottom * rh;
    LandFloat bw = border->right * rw;
    LandFloat bh = border->bottom * rh;

    LandVector spRect = {
        xpos + (border->left * rw),
        ypos + (border->top * rh),
        xpos + fw - bw,
        xpos + fh - bh,
    };
    LandVertex* vertex = draw->vertex;
    sprite->offset = landGetListTotal(vertex->indexs);
    unsigned offset = landGetListTotal(vertex->points);
    sprite->count = landPushObjRect(draw->vertex,
        &spRect, &slot->uv, color, &offset, &obj->transform);
}

static void
doDraw(LandScene* scene, LandObj* obj){
    LandSprite* sprite = (LandSprite*)obj;
    if (!sprite->slot) {
        return;
    }
    LandFrameSlot* slot = sprite->slot;
    LandDraw* draw = &ctx.draw;
    if (scene->draw) {
        if (scene->draw == draw && ctx.drawTex == slot->texture) {
            draw->count += sprite->count;
            return;
        }
        landDraw(scene->canvas, scene->draw);
    }

    draw->offset = sprite->offset;
    draw->count = sprite->count;
    ctx.drawTex = slot->texture;
    landUniformTex(draw, ctx.texPos, slot->texture);
    scene->draw = draw;
}

static void
updateSize(LandSprite* sprite) {
    LandFrameSlot* slot = sprite->slot;
    if (!slot) {
        landObjSize(sprite, 0, 0);
    }
    else {
        landObjSize(sprite, slot->width, slot->height);
    }
}

static void
freeSprite(LandObj* obj) {
    LandSprite* sprite = (LandSprite*)obj;
    landReleaseRef(sprite->frame);
}

LAND_2D_API LandSprite*
landNewSprite(LandFrame* frame) {
    LandSprite* sprite = landNewExtendObj(sizeof(LandSprite), freeSprite);
    landRetainRef(frame);
    sprite->frame = frame;
    sprite->slot = landGetFrameSlot(frame, 0);
    sprite->obj.preDraw = preDraw;
    sprite->obj.draw = doDraw;
    updateSize(sprite);
    return sprite;
}

LAND_2D_API void
landSpriteFrame(LandSprite* sprite, LandFrame* frame) {
    landReleaseRef(sprite->frame);
    landRetainRef(frame);
    sprite->frame = frame;
    updateSize(sprite);
}

LAND_2D_API void
landSpriteIndex(LandSprite* sprite, unsigned index) {
    sprite->slot = landGetFrameSlot(sprite->frame, index);
    updateSize(sprite);
}


int
landInitSpriteContext(){
    LandDraw* draw = &ctx.draw;
    draw->depthTest = LAND_DEPTH_TEST_ALWAYS;
    draw->drawType = LAND_DRAW_TRIANGLES;
    draw->shader = landNewStdShader(LAND_STD_SHADER_COLOR_TEXTURE);
    draw->vertex = landNewVertex(32);
    landRetainRef(draw->shader);
    landRetainRef(draw->vertex);
    ctx.texPos = landRegUniformTex(draw, "landBaseTexture", NULL);

    LandSceneMod mod = {
        beginDraw,
        readyDraw,
    };
    landPushSceneMod(&mod);
    return 0;
}